home *** CD-ROM | disk | FTP | other *** search
/ Super PC 31 / Super PC 31 (Shareware).iso / spc / inter / winpm223 / forms / ph / ph.c next >
Encoding:
C/C++ Source or Header  |  1995-06-16  |  15.5 KB  |  447 lines

  1. //
  2. //  PH.C
  3. //  Pegasus Mail for Windows extension providing a PH (QI/CSO)
  4. //  interface.
  5. //
  6. //  Copyright (c) 1994, David Harris, All Rights Reserved.
  7. //
  8. //  The author grants explicit permission for this source code to be
  9. //  used or modified as required, subject only to the conditions that
  10. //  the copyright notices above are preserved and that by using this
  11. //  code you agree that the code is provided without warranty of any
  12. //  kind, either explicit or implied, and you use it at your own
  13. //  risk.
  14. //
  15.  
  16. #define STRICT
  17. #include <windows.h>
  18. #include <bwcc.h>
  19. #include <stdio.h>
  20. #include <string.h>
  21. #include <alloc.h>
  22. #include "..\wpmforms.h"
  23. #include "ph.h"               //  Dialog control item IDs
  24.  
  25. int atoi (char *s);
  26.  
  27. //  Form dialogs and callbacks: a form DLL can export a function
  28. //  which WinPMail's form manager will call with Windows messages
  29. //  destined for the form's dialog or window (such as menu selections
  30. //  and so forth). The function takes the following form:
  31.  
  32. typedef long pascal far (*FORM_CALLBACK) (HWND hWnd, WORD wMsg,
  33.    WPARAM wParam, LPARAM lParam);
  34.  
  35. //  The return from a form callback is passed back through the
  36. //  Windows call chain if non-zero, otherwise the default window
  37. //  proc is called. The reasons we call this function instead of
  38. //  simply sending messages to the dialog itself are:
  39. //
  40. //   1: Dialogs can only return Boolean values, whereas the callback
  41. //      can provide the long value Windows expects to pass back
  42. //      through the calling chain.
  43. //
  44. //   2: Having a separate callback means that it is potentially
  45. //      possible to have a form which does not create a dialog.
  46. //
  47. //
  48. //  Minimum interface: the minimum interface a form DLL must provide
  49. //  is a routine called "forminit" which is exported by name. "forminit"
  50. //  has the following prototype:
  51. //
  52. //  WORD FAR PASCAL _export FORMINIT (WORD version, int variant, HWND hParent,
  53. //      char *data, HWND *hDialog, char *callback_name);
  54. //
  55. //  "version" is passed in with the version of the WinPMail forms
  56. //     manager which is running.
  57. //  "variant" indicates what type of form is required - the following
  58. //     values are currently defined:
  59. //       0: Create a form for composing a message
  60. //       1: Create a form for reading a message
  61. //  "hParent" contains the handle of the WinPMail MDI child window
  62. //     which is to contain the form.
  63. //  "data" contains any string defined as being required for the
  64. //     form in the menu interface.
  65. //  "hDialog" should be filled in with the window handle of the
  66. //     modeless dialog created within the MDI child window.
  67. //  "callback_name" (optional) should be filled in with the name of the
  68. //     function in the DLL of the exported function to which messages
  69. //     should be sent or NULL if there is none. If NULL, messages are
  70. //     sent to the dialog returned in "hDialog". You will use an
  71. //     indirect callback of this kind when your extension does not
  72. //     create a dialog within the enclosing parent window.
  73. //
  74. //  When forminit is called, the DLL should register any window
  75. //  classes it needs then create the dialog within the MDI parent
  76. //  window and size it to the correct size. On return WinPMail will
  77. //  resize the parent window to enclose the dialog correctly. The
  78. //  DLL should NOT make the dialog visible - WinPMail will do that
  79. //  as required.
  80.  
  81. #define WM_MARGIN (WM_USER + 1099)
  82. #define WM_SETDIR (WM_USER + 400)
  83. #define WM_ADDFILE (WM_USER + 401)
  84. #define WM_STARTUP (WM_USER + 403)
  85. #define WM_GET_ALIST (WM_USER + 404)
  86.  
  87. #define IDC_QUICKHELP_TO   970
  88. #define IDC_QUICKHELP_CC   971
  89.  
  90.  
  91. int register_form_classes (void);
  92.  
  93.  
  94. HFONT hPhFont;
  95. int ec_xdelta, ec_ydelta;
  96. HINSTANCE  hLibInstance;            // set in LibMain, used throughout the DLL
  97. char szFormDlgName [] = "PH";       // Name of dialog resource template.
  98. char szFormDlgClassName [] =
  99.    "bordlg_ph";                     // Class name for the form dialog.
  100. HWND last_focus;                    // The last control to have focus
  101. char no_tcp [] =
  102.    "This extension can only run on systems where WinPMail "
  103.    "has access to Winsock TCP/IP services.";
  104.  
  105. #pragma warn -par
  106.  
  107.  
  108. void GetLocalWindowRect (HWND hWnd, RECT *r)
  109.    {
  110.    //  A utility function which retrieves the bounding rectangle
  111.    //  for a Child window expressed in the coordinate system of
  112.    //  its parent.
  113.  
  114.    HWND hParent;
  115.    RECT r2;
  116.    int width, height;
  117.  
  118.    GetWindowRect (GetParent (hWnd), &r2);
  119.    GetWindowRect (hWnd, r);
  120.    height = r->bottom - r->top;
  121.    width = r->right - r->left;
  122.    r->left -= r2.left;
  123.    r->top -= r2.top;
  124.    r->bottom = r->top + height;
  125.    r->right = r->left + width;
  126.    }
  127.  
  128.  
  129. WORD FAR PASCAL _export FORMINIT (WORD version, int variant, HWND hParent,
  130.    char *data, HWND *hDialog, char *callback_name)
  131.    {
  132.    RECT r, r2;
  133.    char szBuf [80];
  134.    int i;
  135.    HWND hControl;
  136.    BYTE tm [7];
  137.  
  138.    //  First, check to see if the version of the form manager is
  139.    //  one we can work with. This check is pretty arbitrary - you
  140.    //  should probably assume that you may not work with any version
  141.    //  where the major version number is higher than the original
  142.    //  version you targeted.
  143.  
  144.    if ((version & 0xFF00) > 0x100) return 0;
  145.  
  146.    //  Now check the variant number; for the PH Query Client, we only
  147.    //  provide a COMPOSER format.
  148.  
  149.    if (variant != 0) return 0;
  150.  
  151.    (*hDialog) = CreateDialog (hLibInstance, (LPCSTR) szFormDlgName, hParent, NULL);
  152.    if ((*hDialog) == NULL) return 0;
  153.  
  154.    GetClientRect (*hDialog, &r);
  155.    MoveWindow (*hDialog, 0, 0, r.right, r.bottom, FALSE);
  156.  
  157.    GetClientRect (*hDialog, &r);
  158.    GetLocalWindowRect (GetDlgItem (*hDialog, 103), &r2);
  159.    ec_xdelta = r.right - r2.right;
  160.    ec_ydelta = r.bottom - r2.bottom;
  161.  
  162.    if (! hPhFont)
  163.       hPhFont = CreateFont (14, 0, 0, 0, 400, FALSE,
  164.          FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS,
  165.          CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
  166.          DEFAULT_PITCH | FF_DONTCARE, "Courier");
  167.    SendMessage (GetDlgItem (*hDialog, 103), WM_SETFONT,
  168.       (WPARAM) hPhFont, (LPARAM) TRUE);
  169.  
  170.    //  Some default values can be passed in the "data" parameter -
  171.    //  for the PH client, we expect it to contain the default PH
  172.    //  server we should use.
  173.  
  174.    if ((data != NULL) && (*data))
  175.      SetDlgItemText (*hDialog, 101, data);      // Default host
  176.    return 1;
  177.    }
  178.  
  179.  
  180. void trim_newline (char *str)
  181.    {
  182.    /*  Remove the terminating newline from a string
  183.    **  if it's present. */
  184.  
  185.    int i;
  186.  
  187.    i = strlen (str) - 1;
  188.    while ((i >= 0) && ((str [i] == '\n') || (str [i] == '\r'))) 
  189.       str [i -- ] = '\0';
  190.    }
  191.  
  192.  
  193. void ph_query (HWND hParent, HWND hControl, char *host, char *search)
  194.    {
  195.    //  Perform a PH query given the server and match string entered
  196.    //  by the user. PH is a simple protocol developed at UIUC which,
  197.    //  while suffering from some inadequacies in data formatting, is
  198.    //  still a useful way of querying users at a site.
  199.  
  200.    char buffer [256], *s;
  201.    int last, i;
  202.  
  203.    if (SendMessage (hParent, WM_F_TCPOPEN, 105, (LPARAM) host) < 1)
  204.       return;
  205.  
  206.    //  Select the entire contents of our "results" edit control so
  207.    //  it will be cleared by any reply from the server.
  208.  
  209.    SendMessage (hControl, EM_SETSEL, 0, 0x7FFF0000);
  210.    SendMessage (hControl, EM_REPLACESEL, 0, (LPARAM) "");
  211.  
  212.    //  We don't do anything fancy - we just use the absolute minimum
  213.    //  form of lookup and we make no particular attempt to parse the
  214.    //  data returned.
  215.  
  216.    sprintf (buffer, "query %s\r\n", search);
  217.    last = 1;
  218.    if (SendMessage (hParent, WM_F_TCPPUTS, 0, (LPARAM) buffer) > 0)
  219.       {
  220.       for (;;)
  221.          {
  222.          buffer [0] = '\0';
  223.          if (SendMessage (hParent, WM_F_TCPGETS, sizeof (buffer), (LPARAM) buffer) < 1)
  224.             break;
  225.          if (strnicmp (buffer, "200:OK", 6) == 0) break;
  226.          if (buffer [0] == '\0') break;
  227.          trim_newline (buffer);
  228.          if ((buffer [0] == '5') && (buffer [3] == ':'))
  229.             {
  230.             SendMessage (hControl, EM_REPLACESEL, 0, (LPARAM) (buffer + 4));
  231.             break;
  232.             }
  233.          for (s = buffer; *s && (*s != ':'); s ++) ;
  234.          if (*s == ':') ++ s;
  235.          i = atoi (s);
  236.          if (i > last)
  237.             {
  238.             SendMessage (hControl, EM_REPLACESEL, 0, (LPARAM) "\r\n");
  239.             last = i;
  240.             }
  241.          SendMessage (hControl, EM_REPLACESEL, 0, (LPARAM) s);
  242.          SendMessage (hControl, EM_REPLACESEL, 0, (LPARAM) "\r\n");
  243.          }
  244.       SendMessage (hParent, WM_F_TCPPUTS, 0, (LPARAM) "quit\r\n");
  245.       SendMessage (hParent, WM_F_TCPGETS, sizeof (buffer), (LPARAM) buffer);
  246.       }
  247.    SendMessage (hParent, WM_F_TCPCLOSE, 0, 0);
  248.    }
  249.  
  250.  
  251. LONG FAR PASCAL _export FormProc (HWND hWnd, WORD wMsg, 
  252.    WORD wParam, LONG lParam)
  253.    {
  254.    //  Service routine for the form's enclosed dialog. This is a
  255.    //  standard windows modeless WndProc.
  256.  
  257.    DWORD dwResult = 0, l;
  258.    BOOL fCallDefProc = TRUE;
  259.    char host [80], search [180], *s, *txt;
  260.    RECT r;
  261.    HWND hControl;
  262.    HCURSOR hOldCursor;
  263.    unsigned int len;
  264.  
  265.    switch (wMsg)
  266.       {
  267.       case WM_FM_INIT :
  268.          //  Check to see that TCP/IP services are actually
  269.          //  available on this system. If they aren't, we simply
  270.          //  post a close message to the parent.
  271.  
  272.          if (SendMessage (GetParent (hWnd), WM_F_TCPPRESENT, 0, 0) == 0)
  273.             {
  274.             MessageBox (hWnd, no_tcp, " PH Client", MB_OK | MB_ICONSTOP);
  275.             PostMessage (GetParent (hWnd), WM_CLOSE, 0, 0);
  276.             }
  277.          break;
  278.  
  279.       case WM_DESTROY :
  280.          //  Get rid of our edit control font when we're destroyed.
  281.  
  282.          if (hPhFont)
  283.             {
  284.             SendMessage (GetDlgItem (hWnd, 103), WM_SETFONT,
  285.                (WPARAM) 0, FALSE);
  286.             DeleteObject (hPhFont);
  287.             }
  288.          break;
  289.  
  290.       case WM_FM_INITFOCUS :
  291.          SetFocus (GetDlgItem (hWnd, 101));
  292.          break;
  293.  
  294.       case WM_FM_RESTOREFOCUS :
  295.          //  There's a glitch in the way the Windows MDI manager
  296.          //  seems to manage focus which means that we can have
  297.          //  focus taken away and not returned when the user does
  298.          //  anything outside our window. WinPMail has some quite
  299.          //  complex logic to deal with this case and will send
  300.          //  this message whenever focus should be restored in
  301.          //  our window. We set focus to the last active control
  302.          //  (which we know from trapping EN_SETFOCUS messages).
  303.  
  304.          if (last_focus) SetFocus (last_focus);
  305.          break;
  306.  
  307.       case WM_FM_SIZE :
  308.          fCallDefProc = FALSE;
  309.          MoveWindow (hWnd, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
  310.          if ((hControl = GetDlgItem (hWnd, 103)) == NULL) break;
  311.          GetLocalWindowRect (hControl, &r);
  312.          MoveWindow (hControl, r.left, r.top,
  313.             LOWORD(lParam) - r.left - ec_xdelta,
  314.             HIWORD(lParam) - r.top - ec_ydelta, TRUE);
  315.  
  316.          // Because of an apparent bug in BWCC, we need to invalidate the entire
  317.          // window on any resize, otherwise occasional vertical black lines get
  318.          // left on the client area...
  319.          InvalidateRect (hWnd, NULL, TRUE);
  320.          break;
  321.  
  322.       case WM_FM_COMMAND :       // Menu selections
  323.          if (! last_focus) break;
  324.          SetFocus (last_focus);
  325.          switch (wParam)
  326.             {
  327.             case IDM_UNDO :
  328.                SendMessage (last_focus, WM_UNDO, 0, 0);
  329.                break;
  330.  
  331.             case IDM_CUT :
  332.                SendMessage (last_focus, WM_CUT, 0, 0);
  333.                break;
  334.  
  335.             case IDM_COPY :
  336.                SendMessage (last_focus, WM_COPY, 0, 0);
  337.                break;
  338.  
  339.             case IDM_PASTE :
  340.                SendMessage (last_focus, WM_PASTE, 0, 0);
  341.                break;
  342.             }
  343.          break;
  344.  
  345.       case WM_COMMAND :
  346.          fCallDefProc = FALSE;
  347.          if (HIWORD (lParam) == EN_SETFOCUS)
  348.             {
  349.             //  We have to trap EN_SETFOCUS messages so we know which
  350.             //  control was active last. When a menu selection is made
  351.             //  our current control will lose focus and because of a
  352.             //  curiosity in Windows' MDI management, we won't get it
  353.             //  back. Instead, WinPMail generates a WM_FM_RESTOREFOCUS
  354.             //  message which signals to us that we should set focus to
  355.             //  whatever the last active control was.
  356.  
  357.             last_focus = (HWND) LOWORD (lParam);
  358.             break;
  359.             }
  360.  
  361.          switch (wParam)
  362.             {
  363.             case 139 :
  364.                GetDlgItemText (hWnd, 101, host, sizeof (host));
  365.                GetDlgItemText (hWnd, 102, search, sizeof (search));
  366.                hOldCursor = SetCursor (LoadCursor (NULL, IDC_WAIT));
  367.                ph_query (GetParent (hWnd), GetDlgItem (hWnd, 103), host, search);
  368.                SetCursor (hOldCursor);
  369.                break;
  370.  
  371.             case 140 :
  372.                len = GetWindowTextLength (GetDlgItem (hWnd, 103));
  373.                if (len)
  374.                   {
  375.                   l = SendDlgItemMessage (hWnd, 103, EM_GETSEL, 0, 0);
  376.                   if ((s = (char *) malloc (len + 16)) == NULL) break;
  377.                   GetWindowText (GetDlgItem (hWnd, 103), s, len);
  378.                   txt = s + LOWORD(l);
  379.                   *(s + HIWORD(l)) = '\0';
  380.                   SendMessage (GetParent (hWnd), WM_F_PASTEDATA, 1, (LPARAM) txt);
  381.                   free (s);
  382.                   }
  383.                break;
  384.             }
  385.          break;
  386.       }
  387.  
  388.    if (fCallDefProc)
  389.       dwResult = BWCCDefDlgProc (hWnd, wMsg, wParam, lParam);
  390.  
  391.    return dwResult;
  392.    }
  393.  
  394.  
  395. #pragma warn -sus
  396.  
  397. void unregister_form_classes (void)
  398.    {
  399.    //  Remove any classes associated with the form; we have the
  400.    //  same problem here as we do with registering the classes
  401.    //  for the DLL - we only want to deregister the classes on
  402.    //  the last time we're unloaded.
  403.  
  404.    if (GetModuleUsage (hLibInstance) > 1) return;      //  Not a problem
  405.    UnregisterClass (szFormDlgClassName, hLibInstance);
  406.    }
  407.  
  408.  
  409. BOOL FAR PASCAL LibMain (HINSTANCE hInst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine)
  410.    {
  411.    WNDCLASS wc;
  412.  
  413.    if (! hLibInstance)
  414.       {
  415.       hLibInstance = hInst;
  416.       BWCCGetVersion ();      //  Forces BWCC to be dynamically loaded.
  417.  
  418.       //  Register any window classes used by the form. Forms will usually
  419.       //  register either one or occasionally two classes which define
  420.       //  the composition and reader dialogs created by the DLL.
  421.       //
  422.       //  There's a gotcha here, of course (aren't there always, in
  423.       //  Windows?)... You can't register a window class more than once,
  424.       //  so if the DLL has already been loaded and the user asks to
  425.       //  create a second instance of the form, we have to be careful
  426.       //  not to re-register the class. We do this by checking the
  427.       //  instance usage counter of the DLL - if it's greater than 1,
  428.       //  then we DON'T register the classes.
  429.  
  430.       wc.style          = WS_CHILD;
  431.       wc.lpfnWndProc    = FormProc;
  432.       wc.cbClsExtra     = 0;
  433.       wc.cbWndExtra     = DLGWINDOWEXTRA;
  434.        wc.hInstance      = hLibInstance;
  435.        wc.hIcon          = NULL;
  436.       wc.hCursor        = LoadCursor (NULL, IDC_ARROW);
  437.       wc.hbrBackground  = (HBRUSH) (COLOR_WINDOW + 1);
  438.       wc.lpszMenuName   = NULL;
  439.       wc.lpszClassName  = szFormDlgClassName;
  440.       if (! RegisterClass (&wc))
  441.          MessageBeep (0);
  442.       }
  443.  
  444.    return (TRUE);             // Initialization went OK
  445.    }
  446.  
  447.